package cluster

import (
	"JoRedis/lib/logger"
	"JoRedis/lib/wait"
	"JoRedis/protocol"
	"io"
	"net"
	"runtime/debug"
)

type RedisClient struct {
	nodeConn          net.Conn
	respWait          wait.Wait
	respPayloadPtrChn chan *protocol.Payload
}

func MakeClusterClient(nodeConn net.Conn) *RedisClient {
	return &RedisClient{
		nodeConn:          nodeConn,
		respPayloadPtrChn: make(chan *protocol.Payload),
	}
}

// Write 向集群节点写入数据
func (this *RedisClient) Write(cmdBytes []byte) *protocol.Payload {
	this.respWait.Add(1)
	defer this.respWait.Done()
	// 向集群节点写入数据
	_, _ = this.nodeConn.Write(cmdBytes)
	// 等待集群节点执行命令的响应
	// TODO 超时
	payloadPtr := <-this.respPayloadPtrChn
	logger.Info("集群返回的结果:", string(payloadPtr.DataReply.ToBytes()))
	return payloadPtr
}

// Read 读取集群节点执行命令的响应
func (this *RedisClient) Read() {
	defer func() {
		if err := recover(); err != nil {
			logger.Error(err, string(debug.Stack()))
		}
	}()
	// 集群节点处理完命令后返回命令处理的结果
	payloadPtrChn := protocol.ParseProtocol(this.nodeConn)

	// 遍历响应命令解析后的通道
	for payloadPtr := range payloadPtrChn {
		if payloadPtr.Err != nil {
			// 客户端连接断开
			if payloadPtr.Err == io.EOF {
				this.Close()
				return
			}
			errReplyPtr := protocol.MakeErrReply(payloadPtr.Err.Error())
			_, err := this.nodeConn.Write(errReplyPtr.ToBytes())
			// 写入失败关闭连接
			if err != nil {
				this.Close()
				return
			}
			continue
		}
		if payloadPtr.DataReply == nil {
			logger.Error("empty payload")
			continue
		}
		this.respPayloadPtrChn <- payloadPtr
	}
}

// Close 关闭集群客户端
func (this *RedisClient) Close() {
	close(this.respPayloadPtrChn)
	this.respWait.Wait()
	_ = this.nodeConn.Close()
}
